﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;

namespace autossl
{
    using ExcelConfig;
    using txcloud.sdk.SSL;
    using yacomp;

    internal class Program
    {
        static int Main(string[] args)
        {
            Console.OutputEncoding = Encoding.UTF8;
            Console.WriteLine("Hello, World!");

            if(string.IsNullOrEmpty(AppConfig.Text("验证密码")))
            {
                Console.Write("Press config password: ");
                var cfgpass = Console.ReadLine();
                if (!string.IsNullOrEmpty(cfgpass)) AppConfig.WriteText("验证密码", cfgpass);
            }

            Console.WriteLine("Read need ssl-cert uris..");
            var cfg = txcloud.sdk.Center.Config;
            var uris = cfg.Array("需申请证书域名列表");
            Console.WriteLine($"GET-> {string.Join("、", uris)}");

            var fileauthPath = cfg.Text("域名验证文件路径");
            Console.WriteLine($"GET-> fileauth path: {fileauthPath}");
            if (!Directory.Exists(fileauthPath)) Directory.CreateDirectory(fileauthPath); 
            var fileauthFileName = cfg.Text("域名验证文件名");
            Console.WriteLine($"GET-> fileauth filename: {fileauthFileName}");
            var fileauthFilePath = Path.Combine(fileauthPath, fileauthFileName);
            //var certzipPath = cfg.Text("证书压缩包存放路径");
            //Console.WriteLine($"GET-> certzip path: {certzipPath}");
            var certunzipPath = cfg.Text("证书解压路径");
            Console.WriteLine($"GET-> certzip path: {certunzipPath}");
            if (!Directory.Exists(certunzipPath)) Directory.CreateDirectory(certunzipPath);
            Console.WriteLine($"Read Certificates");
            var exists_certId = cfg.Array("已保存证书列表");
            Console.WriteLine($"GET-> exists_certId: {string.Join("、", exists_certId)}");
            var certctl = new Certificate();
            var allcert = certctl.ReadCertificates();

            var saved = new List<string>();
            foreach (var uri in uris)
            {
                // 最近一次申请
                var certId = allcert.Where(t => (t.Domain.Trim() == uri.Trim() || t.Domain.Trim() == "www." + uri.Trim()) && t.IsExpiring != true && (t.Status == 0 || t.Status == 1 || t.Status == 13)).OrderByDescending(t => t.InsertTime).Select(t => t.CertificateId).FirstOrDefault();
                Console.WriteLine($"==============={uri} certId: {certId}===============");

                if (certId == null)
                {
                    //申请
                    Console.WriteLine($"ApplyCert Now uri certId: {certId}");
                    certId = certctl.ApplyCert(uri.Trim());
                    Console.WriteLine($"Get certId: {certId}");
                }

                if (exists_certId.Contains(certId))
                {
                    saved.Add(certId);
                    Console.WriteLine($"exist certId: {certId}");
                    continue;
                }

                Console.WriteLine($"Read Cert Now..");
                var cert = certctl.ReadCertificateDetail(certId);
                if (cert == null) continue;
                if (cert.Status == 0)
                {
                    if (cert.VerifyType != Certificate.DvAuthMethod_FILE)
                    {
                        Console.WriteLine($"Change to FILE Verification");
                        certctl.ToFileCertVerification(certId);
                    }

                    Console.WriteLine($"Read FILE Verification");
                    var fileauth_text = certctl.ReadFileAuth(certId);
                    Console.WriteLine($"Write FILE Verification");
                    if (File.Exists(fileauthFilePath)) File.Delete(fileauthFilePath);
                    using (var fileauth = File.Create(fileauthFilePath))
                    {
                        fileauth.Write(Encoding.ASCII.GetBytes(fileauth_text));
                        fileauth.Flush();
                    }

                    Console.WriteLine($"Write OK.");

                    Console.WriteLine($"Start FILE Verification");
                    int maxretry = 59;
                    while (!certctl.CheckDomainLocalVerification(certId))
                    {
                        Thread.Sleep(1000);
                        if (maxretry-- < 0) return -1;
                    }
                    Console.WriteLine($"Local Verification OK.");
                    Console.WriteLine($"Apply FILE Verification Ca");
                    maxretry = 59;
                    Console.WriteLine($"Start FILE Verification Ca");
                    while (!certctl.CheckDomainVerification(certId))
                    {
                        Thread.Sleep(1000);
                        if (maxretry-- < 0) return -1;
                    }
                    Console.WriteLine($"Ca Verification OK.");
                    certctl.ApplyCertVerification(certId);
                    Console.WriteLine($"Wait Cert..");

                    maxretry = 128;
                    while (certctl.ReadCertificateDetail(certId).Status != 1)
                    {
                        Thread.Sleep(1000);
                        if (maxretry-- < 0) return -1;
                    }
                    Console.WriteLine($"Cert OK.");
                }
                Console.WriteLine($"Download Cert");
                var certPath = Path.Combine(certunzipPath, uri.Trim());
                if (!Directory.Exists(certPath)) Directory.CreateDirectory(certPath);
                using (var certsr = certctl.DownloadCertStream(certId)) if (certsr != null) Unzip(certsr, certPath, uri.Trim());
                saved.Add(certId);
            }
            cfg.WriteText("已保存证书列表", string.Join(";", saved));
            if (File.Exists(fileauthFilePath)) File.Delete(fileauthFilePath);
            Console.WriteLine("清理过期证书");
            foreach (var cert in allcert)
            {
                if (cert.Status == 3)
                {

                    Console.WriteLine($"Delete cert: {cert.Domain}({cert.CertificateId})");
                    certctl.DeleteCertificate(cert.CertificateId);
                    Console.WriteLine($"delete cert ok.");
                    continue;
                }
                if (cert.Status == 7 || cert.Status == 10)
                {
                    Console.WriteLine($"Delete cert: {cert.Domain}({cert.CertificateId})");
                    certctl.DeleteCertificate(cert.CertificateId);
                    Console.WriteLine($"delete cert ok.");
                    continue;
                }

            }
            Console.WriteLine($"Bye-");
            return 0;
        }


        internal static void Unzip(Stream zip, string path, string domain_name)
        {
            // 只取 xxx.key   xxx.pem   xxx.csr   xxx.pfx
            domain_name = domain_name.ToLower();
            var get_filenames = new string[8];
            get_filenames[0] = $"{domain_name}.key";
            get_filenames[1] = $"{domain_name}.pem";
            get_filenames[2] = $"{domain_name}.csr";
            get_filenames[3] = $"{domain_name}.pfx";
            if (domain_name.StartsWith("www.")) for (var i = 4; i < 8; i++) get_filenames[i] = get_filenames[i - 4].Substring(4);
            else for (var i = 4; i < 8; i++) get_filenames[i] = "www."+ get_filenames[i - 4];

            zip.Seek(0, SeekOrigin.Begin);
            using (var f = new YacompZip(zip))
            {
                var disk = f.GetStorage();
                foreach (var _file in disk.LoopFiles())
                {
                    if (!get_filenames.Contains(_file.FileName.ToLower())) continue;

                    var _path = Path.Combine(path, $"{domain_name}.{_file.FileExtension}");
                    if (File.Exists(_path)) File.Delete(_path);
                    _file.ToStream(File.Create(_path));
                }
            }
        }
    }
}
